package com.tn.exam.dao.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.tn.exam.dao.BaseDao;
import com.tn.exam.help.orm.Page;
import com.tn.exam.util.reflection.ReflectionUtils;


public class BaseDaoImpl<T> extends HibernateDaoSupport implements BaseDao{
	
	protected Logger log = LoggerFactory.getLogger(getClass());
	
	private Class<T> entityClass;
	
	public BaseDaoImpl(){
		entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
		log.info(entityClass.getSimpleName());
	}
	
	@Resource(name = "sessionFactory")
	public void setSupperSessionFactory(SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}
	
	public int cudByHql(String hql, Map<String, Object> map) {
		return this.getQuery(hql, map).executeUpdate();
	}
	
	public int cudByHql(String hql) {
		// TODO Auto-generated method stub
		return getHibernateTemplate().bulkUpdate(hql);
	}
	
	public int cudBySql(String sql, Map<String, Object> map) {
		return this.getSQLQuery(sql, map).executeUpdate();
	}

	
	public void delete(Serializable id) {
		getHibernateTemplate().delete(this.load( id));
	}
	
	public void delete(Object obj){
		getHibernateTemplate().delete(obj);
	}

	
	public void deleteAll(Collection<?> collection) {
		getHibernateTemplate().deleteAll(collection);
	}

	
	public List<?> findByCriteria(Criteria criteria,
			Map<String, String> orderByMap, int pageSize, int pageNo) {
		return getCriteria(criteria, orderByMap, pageSize, pageNo).list();
	}

	
	public List<?> findByHql(String hql, Map<String, Object> map, int pageSize,
			int pageNo) {
		return this.getQuery(hql, map, pageSize, pageNo).list();
	}

	
	public List<?> findByPropertiesEq(Class<?> objClass,Map<String, Object> proMap,
			Map<String, String> orderByMap, int pageSize, int pageNo) {
		Criteria criteria = this.createCriteria();
		criteria = this.createCriteriaExpressionEq(criteria, proMap);
		return getCriteria(criteria, orderByMap, pageSize, pageNo).list();
	}

	
	public List<?> findBySql(String sql, Map<String, Object> map, int pageSize,
			int pageNo) {
		return this.getSQLQuery(sql, map, pageSize, pageNo).list();
	}

	
	public Object get(Serializable id) {
		return getHibernateTemplate().get(this.entityClass, id);
	}

	
	public Object getCountByHql(String hql, Map<String, Object> map,
			int pageSize, int pageNo) {
		String totalHql = hql.substring( hql.toLowerCase().indexOf("from"), hql.length()); 
		totalHql = "SELECT COUNT(*) "+totalHql;
		return this.findByHql(totalHql, map, pageSize, pageNo).get(0);
	}

	
	public Object getCountBySql(String sql, Map<String, Object> map, int pageSize,
			int pageNo) {
		String totalHql = sql.substring( sql.toLowerCase().indexOf("from"), sql.length()); 
		totalHql = "SELECT COUNT(*) "+totalHql;
		return this.findBySql(sql, map, pageSize, pageNo).get(0);
	}

	
	public void insert(Object obj) {
		super.getHibernateTemplate().save(obj);
	}

	
	public Object load(Serializable id) {
		return super.getHibernateTemplate().load(this.entityClass, id);
	}

	
	public void saveOrUpdate(Object obj) {
		super.getHibernateTemplate().saveOrUpdate(obj);
	}

	
	public void saveOrUpdateAll(Collection<?> collection) {
		super.getHibernateTemplate().saveOrUpdateAll(collection);
	}

	
	public void update(Object obj) {
		super.getHibernateTemplate().update(obj);
	}
	
	
	
	
	/*
	 * ----------------------private method---------------------
	 */
	/**
	 * HQL
	 */
	private Query getQuery(String hql, Map<String, Object> map) {
		Query query = this.createQuery(hql);
		query = this.setParameter(query, map);
		return query;
	}
	protected Query createQuery(String hql) {
		return getSession().createQuery(hql);
	}
	private Query setParameter(Query query, Map<String, Object> map) {
		if (map != null) {
			Set<String> keySet = map.keySet();
			for (String string : keySet) {
				Object obj = map.get(string);
				if(obj instanceof Collection<?>){
					query.setParameterList(string, (Collection<?>)obj);
				}else if(obj instanceof Object[]){
					query.setParameterList(string, (Object[])obj);
				}else{
					query.setParameter(string, obj);
				}
			}
		}
		return query;
	}
	private Query getQuery(String hql, Map<String, Object> map, int pageSize,
			int pageNo) {
		Query query = this.createQuery(hql);
		query = this.setParameter(query, map);
		query = this.setPageProperty(query, pageSize, pageNo);
		return query;
	}
	/**
	 * ��Query���������÷�ҳ����
	 */
	private Query setPageProperty(Query query, int pageSize, int pageNo) {
		if (pageNo != 0 && pageSize != 0) {
			query.setFirstResult((pageNo - 1) * pageSize);
			query.setMaxResults(pageSize);
		}
		return query;
	}
	
	/**
	 * SQL
	 */
	private SQLQuery getSQLQuery(String sql, Map<String, Object> map) {
		SQLQuery query = this.createSQLQuery(sql);
		query = this.setParameter(query, map);
		return query;
	}
	private SQLQuery createSQLQuery(String sql) {
		return getSession().createSQLQuery(sql);
	}
	private SQLQuery setParameter(SQLQuery query, Map<String, Object> map) {
		if (map != null) {
			Set<String> keySet = map.keySet();
			for (String string : keySet) {
				Object obj = map.get(string);
				//���￼�Ǵ���Ĳ�����ʲô���ͣ���ͬ����ʹ�õķ�����ͬ
				if(obj instanceof Collection<?>){
					query.setParameterList(string, (Collection<?>)obj);
				}else if(obj instanceof Object[]){
					query.setParameterList(string, (Object[])obj);
				}else{
					query.setParameter(string, obj);
				}
			}
		}
		return query;
	}
	private SQLQuery getSQLQuery(String sql, Map<String, Object> map,
			int pageSize, int pageNo) {
		SQLQuery query = this.createSQLQuery(sql);
		query = this.setParameter(query, map);
		query = this.setPageProperty(query, pageSize, pageNo);
		return query;
	}
	private SQLQuery setPageProperty(SQLQuery query, int pageSize, int pageNo) {
		if (pageNo != 0 && pageSize != 0) {
			query.setFirstResult((pageNo - 1) * pageSize);
			query.setMaxResults(pageSize);
		}
		return query;
	}
	
	
	/**
	 *Criteria
	 */
	/**
	 * ������ѯ-����������
	 */
	private Criteria createOrderBy(Criteria criteria,
			Map<String, String> orderByMap) {
		if (orderByMap != null) {
			Set<String> keySet = orderByMap.keySet();
			for (String string : keySet) {
				this.createOrderBy(criteria, string, orderByMap
						.get(string));
			}
		}
		return criteria;
	}
	/**
	 * ������ѯ-����
	 */
	private Criteria createOrderBy(Criteria criteria, String orderByKey,
			String orderByType) {
//		if (Constant4Dao.DESC.equalsIgnoreCase(orderByType.trim())) {
//			criteria.addOrder(Order.desc(orderByKey));
//		} else {
//			criteria.addOrder(Order.asc(orderByKey));
//		}
		return criteria;
	}
	/**
	 * ������ѯ-��ҳ
	 */
	private Criteria createPaging(Criteria criteria, int pageSize, int pageNo) {
		if (pageSize != 0 && pageNo != 0) {
			criteria.setFirstResult((pageNo - 1) * pageSize);
			criteria.setMaxResults(pageSize);
		}
		return criteria;
	}
	private Criteria getCriteria(Criteria criteria,
			Map<String, String> orderByMap, int pageSize, int pageNo) {
		//����
		criteria = this.createOrderBy(criteria, orderByMap);
		//��ҳ
		criteria = this.createPaging(criteria, pageSize, pageNo);
		return criteria;
	}
	/**
	 * ����������ѯ����
	 */
	protected Criteria createCriteria() {
		return this.getSession().createCriteria(entityClass);
	}
	private Criteria createCriteriaExpressionEq(Criteria criteria,Map<String, Object> proMap){
		if(proMap!=null){
			Set<String> keySet = proMap.keySet();
			for (String string : keySet) {
				criteria.add(Restrictions.eq(string, proMap.get(string)));
			}
		}		
		return criteria;
	}
	
	public int getAllTotal() {
		String hql = "select count(*) from " + this.entityClass.getSimpleName();
		Query query = this.createQuery(hql);
		return ((Long)query.uniqueResult()).intValue();
	}
	
	public int getTotal(String hql) {
		return this.getAll(hql).size();
	}
	
	public List getAll() {	
		Criteria criteria = this.createCriteria();
		return criteria.list();
	}
	
	public List getAll(String hql) {
		Query query = this.createQuery(hql);
		return query.list();
	}
	
	public List queryForPage(String hql, int page, int rows) {
		// TODO Auto-generated method stub
		Query query = getSession().createQuery(hql);
		query.setFirstResult(page);
		query.setMaxResults(rows);
		return query.list();
	}
	
	public List getByProperty(String attrName, Object value) {
		String hql = null;
		if(value instanceof String){
			hql = "from " + entityClass.getSimpleName() + " as modul where modul." + attrName + "= '" + value + "'";
		}else{
			hql = "from " + entityClass.getSimpleName() + " as modul where modul." + attrName + "= " + value;
		}
		return getSession().createQuery(hql).list();
	}

	public Object getUniqueByProperty(String attrName, Object value) {
		Criteria query = this.createCriteria();
		query.add(Restrictions.eq(attrName, value));
		List result = query.list();
		if(result == null || result.size() == 0){
			return null;
		}else if(result.size() == 1){
			return result.get(0);
		}else{
			throw new RuntimeException("调用属性值唯一方法出现多个数据，数据有错或者方法调用错误。");
		}
	}

	public List queryForPage(int page, int rows) {
		Criteria query = getSession().createCriteria(entityClass);
		query.setFirstResult(page);
		query.setMaxResults(rows);
		return query.list();
	}
	
}
